import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

public class Simulator {

	ArrayList<Agent> agents = new ArrayList<Agent>();
	Table table;
	boolean finished = false;
	InputStreamReader input = new InputStreamReader(System.in);
	BufferedReader reader = new BufferedReader(input);
	int agentsLeft = 0;
	int totalGames = 5;
	int gamesPlayed = 0;
	public static boolean DEBUG = false;
	
	
	

	//Modifiers

	boolean noInput = false;
	boolean noOutput = false;
	boolean fullGame = false;

	public Simulator()
	{

	}

	public void loadAgents(String[] args)
	{
		int i = 0;
		/*while (i <= 2)
		{
			i++;
			agents.add((new MonteCarloAgent()).setName(""+i));
		}*/
	//	agents.add(new MonteCarloAgent().setName("MonteCarlo"));
		agents.add(new ConservativeAgent().setName("Conservative"));
	//	agents.add(new ActionAgent().setName("Action"));
	//	agents.add(new WinAgent().setName("Win"));
	//	agents.add(new ControlAgent().setName("Control"));
		agents.add(new BetterAgent().setName("Better"));
	//	agents.add(new BluffAgent().setName("Bluff"));
	//	
		
	
		agentsLeft = agents.size();

	}

	public void initialize()
	{
		ArrayList<Card> cards= new ArrayList<Card>();
		for (int value = 2; value <= 14; value++)
		{
			for (Card.Suit suit : Card.Suit.values())
			{
				cards.add(new Card(suit, value));
			}
		}
		Collections.shuffle(cards);

		for (Agent agt : agents)
		{
			agt.newHand();
			for (int i = 0; i < 2; i++)
			{
				Card card = cards.remove(0);
				agt.dealCard(card);
			}
		}

		table = new Table(agents, cards);

		for (Agent agt: agents)
			agt.initialize(table);

	}

	public void startSimulation()
	{
		Tester.runTests();
		gamesPlayed = 0;
		System.out.println("Starting game: "+(gamesPlayed + 1)+" out of "+totalGames);
		while (gamesPlayed < totalGames)
			runGame();
		System.out.println("All games finished");
		System.out.println(table.toString());
		
	}

	public void runGame()
	{
		ArrayList<String> modifiers = new ArrayList<String>();
		initialize();

		if (!fullGame)
		getInput(modifiers);
		while (!finished)
			runRound(modifiers);
		modifiers = new ArrayList<String>();
		if (!fullGame)
		{
			noInput = false;
			noOutput = false;
		}
		agentsLeft = agents.size();
		gamesPlayed++;
		System.out.println("A total of "+gamesPlayed+" games have been finished ("+Run.currentSim+")");
		finished = false;
		for (Agent agent : agents)
			agent.setFold(false);

	}

	public void runRound(ArrayList<String> modifiers)
	{

		for (String mod : modifiers)
		{
			if (mod.equals("no-input"))
				noInput = true;
			if (mod.equals("no-output"))
			{
				noOutput = true;
				noInput = true;
			}
			if (mod.equals("full"))
			{
				fullGame = true;
				noInput = true;
				noOutput = true;
			}
		}
		
		
		//Initialize roundBet
		table.resetRoundBet();
		
		if (!noOutput)
		{
			System.out.println("Round Started");
			System.out.println("-------------------------");
			System.out.println("Table:");
			System.out.println(table.toString());
			System.out.println("-------------------------");
		}
		int turnsTaken = 0;
		int index = 0;
		while (turnsTaken < agents.size() || !allCalled(table.getMaxBet()))
		{
			Agent agent = agents.get(index);
			if (agent.getFold())
			{
				index = (index + 1)%agents.size();
				continue;
			}
			if (!noOutput)
				System.out.println(agent.toString()+"'s turn");
			Action action = agent.takeTurn();
			switch (action.type)
			{
			case FOLD:
				action = foldAgent(agent);
				break;

			case RAISE:

				int agentBet = table.bets.get(agent);
				int amountRaised = action.value - agentBet;

				if (action.value < table.getMaxBet())
				{
					if (!noOutput)
						System.out.println(agent.toString()+" tried to raise to "+action.value+", but bet too low");
					action = foldAgent(agent);
					break;
				}

				if (agent.money < amountRaised)
				{
					if (!noOutput)
						System.out.println(agent.toString()+" tried to raise, but did not have "+amountRaised);
					action = foldAgent(agent);
					break;
				}

				agent.addMoney(-amountRaised);
				table.bets.put(agent, action.value);
				table.addRoundBet(amountRaised);
				break;

			case CALL:
				int oldBet = table.bets.get(agent);
				int callDifference = table.getMaxBet() - oldBet;
				if (callDifference <= 0)
					break;
				if (agent.money < callDifference)
				{
					if (!noOutput)
						System.out.println(agent.toString()+" tried to call, but did not have "+callDifference);
					action = foldAgent(agent);
					break;
				}

				agent.addMoney(-callDifference);
				table.bets.put(agent, table.getMaxBet());

				break;
			}
			if (!noOutput)
				System.out.println("Agent "+agent.name +" "+action.toString());
			if (!noOutput)
				agent.printMoney();

			turnsTaken++;

			if (!noInput)
			{
				getInput(modifiers);
			}

			index = (index + 1)%agents.size();

		}

		table.endRound(this);
	}

	private Action foldAgent(Agent agent)
	{
		agent.setFold(true);
		agentsLeft--;
		System.out.println("There are "+agentsLeft+" agents left");
		if (agentsLeft == 1)
			endGame();

		return Action.foldAction();
	}

	private boolean allCalled(int bet)
	{
		for (Agent agt: agents)
		{
			if (agt.getFold())
				continue;
			if (table.bets.get(agt) < bet)
				return false;
		}
		return true;
	}

	public void getInput(ArrayList<String> modifiers)
	{
		System.out.print(">");

		try{
			String console = reader.readLine();
			while (console.length() > 0)
			{
				console = console.replace("\n", "");
				if (console.length() == 0)
					break;
				if (console.equals("?"))
				{
					System.out.println("Continue to the next round by typing nothing and pressing Enter");
					System.out.println("");

				}
				if (console.equals("no-input"))
				{
					modifiers.add("no-input");
					break;
				}
				if (console.equals("no-output"))
				{
					modifiers.add("no-output");
					break;
				}
				if (console.equals("full"))
				{
					modifiers.add("full");
					break;
				}
				System.out.print(">");
				console = reader.readLine();

			}
		}
		catch (Exception e){System.out.println(e.getStackTrace());
		}
	}

	public void endGame()
	{
		System.out.println("Game Finished");
		Agent winner = table.getWinner();
		winner.addMoney(table.getPot());
		System.out.println("Winner: "+winner.toString());
		Hand winningHand = Hand.getBestHand(winner.getFullCards());
		if (winningHand == null)
		System.out.println("Won by everyone else folding");
		else
		{
			System.out.println("Winning cards: "+winner.getFullCards().toString());
			System.out.println("Winning hand: "+winningHand.toString());
		}
		System.out.println(table.toString());
		finished = true;
	}

}
